import { extend } from "../shared";

// 获取当前执行的 effect
let activeEeffect;

let shouldTrack;

export class ReactiveEffect {
    private _fn: any;
    deps = [];
    // 是否激活状态，用于清除依赖优化用
    active = true;
    public scheduler: Function | undefined;
    onStop?: () => void;

    // scheduler是可选的，是调度器，用来控制effect的执行
    constructor(fn, scheduler?: Function) {
        this._fn = fn;
        this.scheduler = scheduler;
    }

    run() {
        if (!this.active) {
            return this._fn();
        }

        // 是否收集依赖，判断key = key + 1的情况
        shouldTrack = true;
        activeEeffect = this;

        const result = this._fn();
        // 重置shouldTrack
        shouldTrack = false;

        return result;
    }

    stop() {
        // 优化性能
        if (this.active) {
            cleanEffect(this);
            if (this.onStop) {
                this.onStop();
            }
            this.active = false;
        }
    }
}

// 清除effect
function cleanEffect(effect) {
    effect.deps.forEach((dep: any) => {
        dep.delete(effect);
    })
    effect.deps.length = 0;
}

const targetMap = new Map();
export function track(target, key) {
    // 判断是否正在收集依赖中
    if (!isTracking()) return;

    // target（目标对象） -> key（对象属性） -> dep（依赖）
    // depsMap（对象key的集合）
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        depsMap = new Map();
        targetMap.set(target, depsMap);
    }

    // dep（key的依赖集合）
    let dep = depsMap.get(key);
    if (!dep) {
        dep = new Set();
        depsMap.set(key, dep);
    }

    trackEffects(dep);
}

export function trackEffects(dep) {
    // 添加依赖
    // 如果dep已经有该effect，则不需要添加
    if (dep.has(activeEeffect)) return;
    dep.add(activeEeffect)

    // 反向存储dep，方便后续清除dep
    activeEeffect.deps.push(dep);
}

// 判断是否正在收集依赖中
export function isTracking() {
    // if (!activeEeffect) return;
    // if (!shouldTrack) return;
    return shouldTrack && activeEeffect !== undefined;
}

export function trigger(target, key) {
    let depsMap = targetMap.get(target);
    let dep = depsMap.get(key);

    triggerEffects(dep);
}

export function triggerEffects(dep) {
    dep.forEach(effect => {
        // 判断是否需要调度
        if (effect.scheduler) {
            effect.scheduler();
        } else {
            effect.run();
        }
    })
}

export function effect(fn, options: any = {}) {
    // fn
    // scheduler调度器，类似异步任务
    const _effect = new ReactiveEffect(fn, options.scheduler);

    // extend
    extend(_effect, options);

    _effect.run();

    const runner: any = _effect.run.bind(_effect);
    runner.effect = _effect;

    return runner;
}

// 手动停止或清理副作用函数
export function stop(runner: any) {
    runner.effect.stop();
}